package utils

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"strings"
	"sync"
	"time"

	"github.com/hashicorp/consul/api"
)

type Blog struct {
	Id         int      `json:"id"`
	UserId     int      `json:"AuthorId"`
	Title      string   `json:"title"`
	Content    string   `json:"content"`
	LikeNum    int      `json:"likeNum"`
	JoyNum     int      `json:"joyNum"`
	JoyFeeling string   `json:"joyFeeling"`
	Place      string   `json:"place"`
	CommentNum int      `json:"commentNum"`
	CreateTime string   `json:"createTime"`
	ReadNum    int      `json:"readNum"`
	Tags       []string `json:"tags"`
	Category   string   `json:"category"`
}

// ConsulServiceDiscovery
type ConsulServiceDiscovery struct {
	consulURL  string
	httpClient *http.Client
	cache      map[string][]*api.ServiceEntry
	cacheMutex sync.RWMutex
	cacheTTL   time.Duration
	lastUpdate time.Time
}

func NewConsulServiceDiscovery(consulAddr string) (*ConsulServiceDiscovery, error) {
	if !strings.HasPrefix(consulAddr, "http") {
		consulAddr = "http://" + consulAddr
	}

	return &ConsulServiceDiscovery{
		consulURL:  consulAddr,
		httpClient: &http.Client{Timeout: 2 * time.Second}, // 2秒超时，与Lua一致
		cache:      make(map[string][]*api.ServiceEntry),
		cacheTTL:   5 * time.Second,
	}, nil
}

// GetService 实现与Lua脚本完全一致的服务发现逻辑
func (c *ConsulServiceDiscovery) GetService(serviceName string) (*api.ServiceEntry, error) {
	// 1. 尝试从缓存获取
	if services, ok := c.getFromCache(serviceName); ok {
		return c.selectRandomInstance(services), nil
	}

	// 2. 直接查询Consul（不再使用consul api库）
	services, err := c.queryConsulDirectly(serviceName)
	if err != nil {
		return nil, fmt.Errorf("failed to query Consul: %w", err)
	}

	// 3. 更新缓存
	c.updateCache(serviceName, services)

	return c.selectRandomInstance(services), nil
}

// queryConsulDirectly 直接通过HTTP查询Consul，与Lua脚本逻辑一致
func (c *ConsulServiceDiscovery) queryConsulDirectly(serviceName string) ([]*api.ServiceEntry, error) {
	url := fmt.Sprintf("%s/v1/health/service/%s?passing", c.consulURL, serviceName)
	fmt.Printf("查询URL: %s", url)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
	}

	var services []*api.ServiceEntry
	if err := json.NewDecoder(resp.Body).Decode(&services); err != nil {
		return nil, fmt.Errorf("failed to decode response: %w", err)
	}

	if len(services) == 0 {
		return nil, errors.New("no available service instances")
	}

	return services, nil
}

// 保留原有的缓存管理方法
func (c *ConsulServiceDiscovery) getFromCache(serviceName string) ([]*api.ServiceEntry, bool) {
	c.cacheMutex.RLock()
	defer c.cacheMutex.RUnlock()

	services, ok := c.cache[serviceName]
	if !ok || time.Since(c.lastUpdate) > c.cacheTTL {
		return nil, false
	}
	return services, true
}

func (c *ConsulServiceDiscovery) updateCache(serviceName string, services []*api.ServiceEntry) {
	c.cacheMutex.Lock()
	defer c.cacheMutex.Unlock()

	c.cache[serviceName] = services
	c.lastUpdate = time.Now()
}

// selectRandomInstance 随机选择实例（与Lua的math.random一致）
func (c *ConsulServiceDiscovery) selectRandomInstance(services []*api.ServiceEntry) *api.ServiceEntry {
	rand.Seed(time.Now().UnixNano())
	return services[rand.Intn(len(services))]
}

// ConsulHTTPClient 修改为更简单的实现
type ConsulHTTPClient struct {
	consulSD    *ConsulServiceDiscovery
	serviceName string
	httpClient  *http.Client
}

func NewConsulHTTPClient(consulAddr, serviceName string) (*ConsulHTTPClient, error) {
	consulSD, err := NewConsulServiceDiscovery(consulAddr)
	if err != nil {
		return nil, err
	}

	return &ConsulHTTPClient{
		consulSD:    consulSD,
		serviceName: serviceName,
		httpClient: &http.Client{
			Timeout: 10 * time.Second,
		},
	}, nil
}

// DoRequest 简化的请求方法
func (c *ConsulHTTPClient) DoRequest(ctx context.Context, method, path string, body []byte) (*http.Response, error) {
	service, err := c.consulSD.GetService(c.serviceName)
	if err != nil {
		return nil, fmt.Errorf("service discovery failed: %w", err)
	}

	url := fmt.Sprintf("http://%s:%d%s", service.Service.Address, service.Service.Port, path)
	fmt.Printf("请求URL: %s", url)
	req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(body))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}

	return resp, nil
}

// SearchClient 保持不变
type SearchClient struct {
	consulClient *ConsulHTTPClient
}

func NewSearchClient(consulAddr string) (*SearchClient, error) {
	client, err := NewConsulHTTPClient(consulAddr, "search-api")
	if err != nil {
		return nil, err
	}

	return &SearchClient{
		consulClient: client,
	}, nil
}

// IndexBlog 保持不变
func (c *SearchClient) IndexBlog(ctx context.Context, blog *Blog) error {
	body, err := json.Marshal(blog)
	if err != nil {
		return fmt.Errorf("marshal blog error: %w", err)
	}
	if err := blog.FormatCreateTime(); err != nil {
		return fmt.Errorf("date format error: %w", err)
	}

	resp, err := c.consulClient.DoRequest(ctx, "POST", "/api/search/inputblog", body)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(resp.Body)
		return fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, string(body))
	}

	return nil
}

// 日期格式化方法
func (b *Blog) FormatCreateTime() error {
	// 尝试解析带时区的ISO8601格式（如：2025-05-25T16:39:01+08:00）
	if t, err := time.Parse(time.RFC3339, b.CreateTime); err == nil {
		b.CreateTime = t.Format("2006-01-02 15:04:05")
		return nil
	}

	// 尝试解析不带时区的格式（如：2025-05-25 16:39:01）
	if t, err := time.Parse("2006-01-02 15:04:05", b.CreateTime); err == nil {
		b.CreateTime = t.Format("2006-01-02 15:04:05") // 保持原格式
		return nil
	}

	// 尝试解析其他常见格式
	if t, err := time.Parse("2006-01-02T15:04:05", b.CreateTime); err == nil {
		b.CreateTime = t.Format("2006-01-02 15:04:05")
		return nil
	}

	return fmt.Errorf("无法识别的时间格式: %s", b.CreateTime)
}
